home *** CD-ROM | disk | FTP | other *** search
- /* dirutil.c - MS-DOS directory reading routines
- *
- * Bdale Garbee, N3EUA, Dave Trulli, NN2Z, and Phil Karn, KA9Q
- * Directory sorting by Mike Chepponis, K3MC
- */
-
- #include <stdio.h>
- #include <stat.h>
- #include "global.h"
-
- #ifndef FALSE
- #define FALSE (0)
- #endif
-
- #ifndef TRUE
- #define TRUE !(FALSE)
- #endif
-
- #define REGFILE (ST_HIDDEN|ST_SYSTEM|ST_DIRECT)
- #define SET_DTA 0x1a
- #define FIND_FIRST 0x4e
- #define FIND_NEXT 0x4f
-
- struct dirent {
- char rsvd[21];
- char attr;
- short ftime;
- short fdate;
- long fsize;
- char fname[13];
- };
- #define NULLENT (struct dirent *)0
-
- struct dirsort {
- struct dirsort *prev;
- struct dirsort *next;
- struct dirent *direntry;
- };
- #define NULLSORT (struct dirsort *)0
-
- /* Create a directory listing in a temp file and return the resulting file
- * descriptor. If full == 1, give a full listing; else return just a list
- * of names.
- */
- FILE *
- dir(path,full)
- char *path;
- int full;
- {
- FILE *fp,*tmpfile();
-
- fp = tmpfile();
- getdir(path,full,fp);
- /* This should be rewind(), but Aztec doesn't have it */
- fseek(fp,0L,0);
- return fp;
- }
-
- /* wildcard filename lookup */
- filedir(name,times,ret_str)
- char *name;
- int times;
- char *ret_str;
- {
- register char *cp,*cp1;
- static struct dirent sbuf;
-
- bdos(SET_DTA,&sbuf); /* Set disk transfer address */
- /* Find matching file */
- if(dos(times == 0 ? FIND_FIRST:FIND_NEXT,0,REGFILE,name,0,0) == -1)
- sbuf.fname[0] = '\0';
-
- /* Copy result to output, forcing to lower case */
- for(cp = ret_str,cp1 = sbuf.fname; cp1 < &sbuf.fname[13] && *cp1 != '\0';)
- *cp++ = tolower(*cp1++);
- *cp = '\0';
- }
- /* do a directory list to the stream
- * full = 0 -> short form, 1 is long
- */
- getdir(path,full,file)
- char *path;
- int full;
- FILE *file;
- {
- struct dirent sbuf;
- struct stat statbuf;
- register char *cp,*cp1;
- char dirtmp[20];
- int command = FIND_FIRST;
- int i = 0;
- int cflag = 0;
-
- char temp_name[13]; /* Holds a filename temporarily */
- int loop_counter;
- int n = 0;
- char line_buf[50]; /* for long dirlist */
- char cbuf[20],cbuf1[20]; /* for making line_buf */
-
- struct dirsort *prev, *head, *here, *new;
- struct dirent *de;
- void dir_sort(), format_fname(), commas(), isfree(), free_clist();
- unsigned short ax,bx,cx,dx;
- unsigned long free_bytes, total_bytes;
- char s_free[11], s_total[11];
- int malloc_lost = FALSE;
-
- /* Root directory is a special case */
- if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
- path = "\\*.*";
-
- /* If arg is a directory, append "\*.*" to it.
- * This is tricky, since the "stat" system call actually
- * calls the DOS "find matching file" function. The stat
- * call therefore returns the attributes for the first matching
- * entry in the directory. If the arg already ends in *.*,
- * stat will match the . entry in the directory and indicate
- * that the argument is a valid directory name. Hence the
- * heuristic check for '*' in the file name. Kludge...
- */
- else if(index(path,'*') == NULLCHAR
- && stat(path,&statbuf) != -1
- && (statbuf.st_attr & ST_DIRECT)) {
- if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
- return -1;
- sprintf(cp,"%s%c%s",path,'\\',"*.*");
- path = cp;
- cflag = 1;
- }
- head = NULLSORT; /* No head of chain yet... */
- for(;;){
- bdos(SET_DTA,&sbuf); /* Set disk transfer address */
- if(dos(command, 0, REGFILE, path, 0, 0) == -1)
- break;
-
- command = FIND_NEXT; /* Got first one already... */
- if(sbuf.fname[0] != '.'){
- /* nuke "." and ".." */
- n++; /* One more entry */
- new = (struct dirsort *) malloc(sizeof(struct dirsort));
- if(new == NULLSORT)
- malloc_lost = TRUE;
- de = (struct dirent *)malloc(sizeof(struct dirent));
- if(de == NULLENT)
- malloc_lost = TRUE;
- if(malloc_lost){
- /* Clean up and call other routine */
- if(new)free(new);
- free_clist(head);
- return getdir_nosort(path,full,file);
- }
- *de = sbuf; /* Copy contents of directory entry struct */
-
- /* Fix up names for easier sorting... pain! */
- strcpy(de->fname," "); /* 11 blanks */
- cp = sbuf.fname;
- cp1 = de->fname;
- do *cp1++ = *cp++; while (*cp && *cp != '.');
- if(*cp++){
- /* If there is an extension */
- cp1 = &(de->fname[8]);
- do *cp1++ = *cp++; while (*cp);
- }
- if(!(int)head){
- /* Make the first one */
- here = head = new;
- head->prev = head->next = NULLSORT;
- } else {
- /* Link on next one */
- new->next = NULLSORT;
- new->prev = here;
- here->next = new;
- here = new;
- }
- new->direntry = de;
- } /* IF on "." */
- } /* infinite FOR loop */
-
- if(head)
- dir_sort(head); /* Make a nice, sorted list */
-
- here = head;
- if(here && full){
- do {
- sbuf = *(here->direntry); /* Copy data for printing */
- format_fname(dirtmp,sbuf.fname,sbuf.attr);
-
- /* Long form, give other info too */
- sprintf(line_buf,"%-13s",dirtmp);
- if(sbuf.attr & ST_DIRECT)
- strcat(line_buf," ");/* 11 spaces */
- else {
- sprintf(cbuf,"%ld",sbuf.fsize);
- commas(cbuf);
- sprintf(cbuf1,"%10s ",cbuf);
- strcat(line_buf,cbuf1); /* Do filesize */
- }
- sprintf(cbuf,"%2d:%02d %2d/%02d/%02d%s",
- (sbuf.ftime >> 11) & 0x1f, /* hour */
- (sbuf.ftime >> 5) & 0x3f, /* minute */
- (sbuf.fdate >> 5) & 0xf, /* month */
- (sbuf.fdate ) & 0x1f, /* day */
- (sbuf.fdate >> 9) + 80, /* year */
- (i^=1) ? " " : "\n");
- strcat(line_buf,cbuf);
- fprintf(file,"%s",line_buf);
- } while (here = here->next);
- if(i & 1)
- fprintf(file,"\n");
- }
- else if(here){
- /* This is the short form; make sure >=1 entry */
- do {
- sbuf = *(here->direntry); /* Copy data for printing */
- format_fname(dirtmp,sbuf.fname,sbuf.attr);
- fprintf(file,"%s\n",dirtmp);
- } while (here = here->next);
- }
- /* Give back all the memory we temporarily needed... */
- free_clist(head);
-
- if(full){
- /* Provide additional information only on DIR */
- ax = 0x3600; /* AH = 36h, AL = 0 (AL not used) */
- dx = 0; /* Default drive */
- isfree(&ax,&bx,&cx,&dx);
-
- free_bytes = (unsigned long)ax * (unsigned long)cx;
- total_bytes = free_bytes * (unsigned long)dx;
- free_bytes *= (unsigned long)bx;
-
- sprintf(s_free,"%ld",free_bytes); commas(s_free);
- sprintf(s_total,"%ld",total_bytes); commas(s_total);
-
-
- if(n)
- sprintf(cbuf,"%d",n);
- else
- strcpy(cbuf,"No");
-
- fprintf(file,"%s file%s. %s bytes free. Disk size %s bytes.\n",
- cbuf,(n==1? "":"s"),s_free,s_total);
- }
- if(cflag)
- free(path);
- return 0;
- }
- /* Change working directory */
- docd(argc,argv)
- int argc;
- char *argv[];
- {
- char dirname[128],*getcwd();
-
- if(argc > 1){
- if(chdir(argv[1]) == -1){
- printf("Can't change directory\n");
- return 1;
- }
- }
- if(getcwd(dirname,0) != NULLCHAR){
- printf("\\%s\n",dirname);
- }
- return 0;
- }
- /* List directory to console */
- dodir(argc,argv)
- int argc;
- char *argv[];
- {
- char *path;
-
- if(argc >= 2){
- path = argv[1];
- } else {
- path = "*.*";
- }
- getdir(path,1,stdout);
- return 0;
- }
-
-
- /*
- * Return a string with commas every 3 positions.
- * If malloc() fails, return original string unmodified.
- * else the original string is replace with the string with commas.
- *
- * The caller must be sure that there is enough room for the resultant
- * string.
- *
- *
- * k3mc 4 Dec 87
- */
- void
- commas(dest)
- char *dest;
- {
- char *src, *core; /* Place holder for malloc */
- unsigned cc; /* The comma counter */
- unsigned len;
-
- len = strlen(dest);
- if( (core = src = (char *)malloc(len+1)) == NULLCHAR)
- return;
-
- strcpy(src,dest); /* Make a copy, so we can muck around */
- cc = (len-1)%3 + 1; /* Tells us when to insert a comma */
-
- while(*src != '\0'){
- *dest++ = *src++;
- if( ((--cc) == 0) && *src ){
- *dest++ = ','; cc = 3;
- }
- }
- free(core);
- *dest = '\0';
- }
- /*
- * This insertion sort adapted from "Writing Efficient Programs" by Jon Louis
- * Bentley, Prentice-Hall 1982, ISBN 0-13-070244-X (paperback) p. 65
- *
- * Run Time (sec) = K * N^2, where K = 21e-6 on my turbo XT clone (SI=2.6).
- * This could be improved to perhaps K * N * log2(N) using Quicksort, but,
- * as Bentley points out, this insertion sort is actually faster for small
- * values of N. His "best" sorting algorithm uses an insertion sort/Quicksort
- * hybrid, with the "cutoff" value being about 30 elements.
- *
- * I have opted for the straight insertion sort because it is quite simple,
- * provably correct, and not a terrible performer when N < 1000, which is the
- * case for most directories.
- */
- void
- dir_sort(head)
- struct dirsort *head;
- {
- struct dirsort *here, *backtrack;
- struct dirent *de_temp;
-
- for(here = head->next; here != NULLSORT; here = here->next){
- backtrack = here;
- de_temp = here->direntry;
- while(backtrack->prev
- && strcmp(de_temp->fname,backtrack->prev->direntry->fname)<0){
- backtrack->direntry = backtrack->prev->direntry;
- backtrack = backtrack->prev;
- }
- backtrack->direntry = de_temp;
- }
- }
-
- void
- format_fname(dest,src,attr)
- char *dest, *src;
- char attr;
- {
- char *cp = src+8;
- int loop_counter;
-
- for(loop_counter=0; loop_counter<8; loop_counter++){
- *dest++ = tolower(*src++);
- if(*src == ' ')break;
- }
- if(strcmp(cp," ") != 0){ /* There is an extension */
- *dest++ = '.';
- for(loop_counter=0; loop_counter<3; loop_counter++){
- *dest++ = tolower(*cp++);
- if(*cp == ' ')break;
- }
- }
- if(attr & ST_DIRECT)*dest++ = '\\';
- *dest = '\0';
- }
- void
- free_clist(head)
- struct dirsort *head;
- {
- struct dirsort *here;
-
- here = head;
- if(here)do{
- free(here->direntry);
- if(head != here){
- free(head);
- head = here;
- }
- } while (here = here->next);
- if(head != here){
- free(head);
- head = here;
- }
- }
-
-
-
- getdir_nosort(path,full,file)
- char *path;
- int full;
- FILE *file;
- {
- struct dirent sbuf;
- struct stat statbuf;
- register char *cp,*cp1;
- char dirtmp[20];
- int command = FIND_FIRST;
- int i = 0;
- int cflag = 0;
-
- char line_buf[50]; /* for long dirlist */
- char cbuf[20],cbuf1[20]; /* for making line_buf */
-
- void format_fname(),commas(),isfree();
- unsigned short ax,bx,cx,dx;
- unsigned long free_bytes, total_bytes;
- char s_free[11], s_total[11];
- int n = 0; /* Number of directory entries */
-
- /* Root directory is a special case */
- if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
- path = "\\*.*";
-
- /* If arg is a directory, append "\*.*" to it.
- * This is tricky, since the "stat" system call actually
- * calls the DOS "find matching file" function. The stat
- * call therefore returns the attributes for the first matching
- * entry in the directory. If the arg already ends in *.*,
- * stat will match the . entry in the directory and indicate
- * that the argument is a valid directory name. Hence the
- * heuristic check for '*' in the file name. Kludge...
- */
- else if(index(path,'*') == NULLCHAR
- && stat(path,&statbuf) != -1
- && (statbuf.st_attr & ST_DIRECT)) {
- if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
- return -1;
- sprintf(cp,"%s%c%s",path,'\\',"*.*");
- path = cp;
- cflag = 1;
- }
- for(;;){
- bdos(SET_DTA,&sbuf); /* Set disk transfer address */
- if(dos(command,0,REGFILE,path,0,0) == -1)
- break;
-
- command = FIND_NEXT; /* Got first one already... */
- if(sbuf.fname[0] != '.'){ /* nuke "." and ".." */
- if(full){
- n++; /* Count 'em */
- format_fname(dirtmp,sbuf.fname,sbuf.attr);
- sprintf(line_buf,"%-13s",dirtmp);
- if(sbuf.attr & ST_DIRECT){
- strcat(line_buf," ");/* 11 spaces */
- } else {
- sprintf(cbuf,"%ld",sbuf.fsize);
- commas(cbuf);
- sprintf(cbuf1,"%10s ",cbuf);
- strcat(line_buf,cbuf1); /* Do filesize */
- }
- sprintf(cbuf,"%2d:%02d %2d/%02d/%02d%s",
- (sbuf.ftime >> 11) & 0x1f, /* hour */
- (sbuf.ftime >> 5) & 0x3f, /* minute */
- (sbuf.fdate >> 5) & 0xf, /* month */
- (sbuf.fdate ) & 0x1f, /* day */
- (sbuf.fdate >> 9) + 80, /* year */
- (i^=1) ? " " : "\n");
- strcat(line_buf,cbuf);
- fprintf(file,"%s",line_buf);
- } else { /* is short form */
- format_fname(dirtmp,sbuf.fname,sbuf.attr);
- fprintf(file,"%-13s\n",dirtmp);
- }
- }
- }
- if(full){
- if(i)
- fprintf(file,"\n");
- ax = 0x3600; /* AH = 36h, AL = 0 (AL not used) */
- dx = 0; /* Default drive */
- isfree(&ax,&bx,&cx,&dx);
- free_bytes = (unsigned long)ax * (unsigned long)cx;
- total_bytes = free_bytes * (unsigned long)dx;
- free_bytes *= (unsigned long)bx;
- sprintf(s_free,"%ld",free_bytes);
- commas(s_free);
- sprintf(s_total,"%ld",total_bytes);
- commas(s_total);
- if(n)
- sprintf(cbuf,"%d",n);
- else
- strcpy(cbuf,"No");
-
- fprintf(file,"%s file%s. %s bytes free. Disk size %s bytes.\n",
- cbuf,(n==1? "":"s"),s_free,s_total);
- }
- if(cflag)
- free(path);
- return 0;
- }
-
-
-